Skip to content

Cygwin: make create_root_entry tolerant of concurrent mount table initialization#333

Open
ilude wants to merge 1 commit intomsys2:msys2-3.6.7from
ilude:fix/add-item-race-condition
Open

Cygwin: make create_root_entry tolerant of concurrent mount table initialization#333
ilude wants to merge 1 commit intomsys2:msys2-3.6.7from
ilude:fix/add-item-race-condition

Conversation

@ilude
Copy link
Copy Markdown

@ilude ilude commented Mar 27, 2026

Problem

When multiple MSYS2 processes start concurrently, create_root_entry()
can crash with:

fatal error - add_item ("\??\C:\Program Files\Git", "/", ...) failed, errno 1

The add_item() call returns EPERM because it finds an existing
immutable / mount entry and MOUNT_OVERRIDE is not set.

This has been reported in several contexts where parallel MSYS2 process
startup is common:

Changes

mount.cc: Add MOUNT_OVERRIDE to create_root_entry()

This is the direct fix. If / already exists in the mount table when
create_root_entry() runs, add_item() now overwrites it instead of
returning EPERM. This makes the root mount initialization idempotent.

shared.cc: Use created flag from open_shared() in user_info::create()

open_shared() already tracks whether the shared memory mapping was
newly created or already existed (via CreateFileMappingW /
ERROR_ALREADY_EXISTS). Currently user_info::create() calls the
overload that discards this flag.

This change switches to the overload that returns it, and calls
initialize() when created is true. This provides an earlier
initialization path for the process that created the shared memory,
before dll_crt0_1() reaches initialize() via the spinlock.

Note: this does not replace the existing spinlock coordination in
initialize(). The normal cold-start path through dll_crt0_1() is
unchanged. This is a supplementary improvement, not the primary fix.

Testing

Built patched msys-2.0.dll from Cygwin 3.6.7 with MSYS2 patches.
Tested on Windows 11 Enterprise with parallel bash spawning from
Claude Code (which fires multiple hook processes per tool invocation).
The crash was occurring consistently before the patch and has not
recurred after installing it.

@ilude ilude force-pushed the fix/add-item-race-condition branch from 2d5b155 to 170595b Compare March 27, 2026 20:34
When multiple Cygwin/MSYS2 processes start concurrently,
create_root_entry() can fail with EPERM because two processes
both attempt to populate the mount table in shared memory.

user_info::create() calls open_shared(), which internally uses
CreateFileMappingW and tracks whether the mapping was newly
created or already existed.  However, create() discards this
information by calling the overload that drops the 'created'
output parameter, relying instead on a spinlock in initialize()
to coordinate.  The spinlock has a 15-second timeout; if the
initializing process is slow (e.g., internal_getpwsid triggering
domain controller lookups), waiting processes time out and
re-initialize, colliding on the immutable "/" mount entry.

Use the open_shared() overload that returns the 'created' flag
to determine whether this process should initialize the shared
memory.  Only the process that actually created the file mapping
calls initialize(); all others find the mapping already exists
and skip initialization.

As a safety net, add MOUNT_OVERRIDE to create_root_entry() so
that add_item() overwrites an existing "/" entry rather than
failing with EPERM.  This makes the initialization idempotent
even if the primary fix is somehow bypassed.

Signed-off-by: Mike Glenn <mglenn@ilude.com>
@ilude ilude force-pushed the fix/add-item-race-condition branch from 170595b to 992d77e Compare March 27, 2026 20:41
@ilude ilude changed the base branch from msys2-3_3_6-release to msys2-3.6.7 March 27, 2026 20:41
@ilude ilude changed the title Cygwin: fix add_item race in concurrent shared memory initialization Cygwin: make create_root_entry tolerant of concurrent mount table initialization Mar 27, 2026
@ilude
Copy link
Copy Markdown
Author

ilude commented Mar 27, 2026

Happy to submit this upstream to cygwin-patches if that's the preferred path. Opened here first since this is where I built and tested against, but understand if this belongs upstream given both files are core Cygwin code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant